#ifndef _TBMATH_H_
#define _TBMATH_H_

#include "TBSettings.h"

// ===========================================
// Includes
// ===========================================

#include <stdlib.h>
#include <math.h>
#include <time.h>

namespace tb
{

	namespace Math
	{

		// ====================================
		// Constants
		// ====================================

		// hmm... pi...

		const float pi			= 3.141592654f;
		const float pi_radians	= pi / 180.f;
		const float pi_degrees	= 180.f / pi;
		const float pi_half		= pi / 2;
		const float pi_two		= pi * 2;
		const float pi_rcp		= 1 / pi;
		const float pi_rcp_two	= 1 / (pi_two);

		// ====================================
		// Basic functions
		// ====================================

		const float fp_amount = (float)(0xFFFF);
		const float fp_amount_inv = 1 / fp_amount;

		//! Returns the fractional part of a float.
		TB_INLINE float Frac(float a_X)
		{
			return (a_X - (float)(int)a_X);

			//return ((int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
		}

		//! Returns the inverse fractional part of a float (1 - x).
		TB_INLINE float InvFrac(float a_X)
		{
			return (1.f - (a_X - (float)(int)a_X));

			//return (0xFFFF - ((int)(a_X * fp_amount) & 0xFFFF)) * fp_amount_inv;
		}

		//! Returns the number rounded to the nearest integer.
		TB_INLINE int Round(float a_X) 
		{ 
			return ((int)(a_X + 0.5f)); 
		} 

		//! Returns the number rounded down to the nearest integer.
		TB_INLINE float Floor(float a_X) 
		{ 
			return (floorf(a_X));
			//return ((float)(int)a_X); 
		}

		//! Returns the number rounded up to the nearest integer.
		TB_INLINE float Ceil(float a_X) 
		{ 
			return (ceilf(a_X));
			//return ((float)((int)(a_X + 1))); 
		}

		//! Returns the absolute value of a number.
		TB_INLINE float Abs(float a_X)
		{
			return (fabsf(a_X));
			//return ((a_X < 0) ? -a_X : a_X);
		}

		//! Returns the square root of a number.
		TB_INLINE float Sqrt(float a_X) 
		{ 
			return (sqrtf(a_X)); 
		}

		//! Returns the inverse square root of a number.
		TB_INLINE float InvSqrt(float a_X) 
		{ 
			return (1.f / sqrtf(a_X)); 
		}

		// ====================================
		// Generic
		// ====================================

		template <typename T>
		TB_INLINE T Min(T a_A, T a_B)
		{
			return (a_A < a_B) ? a_A : a_B;
		}

		template <typename T>
		TB_INLINE T Min(T a_A, T a_B, T a_C)
		{
			if (a_A < a_B) { return (a_A < a_C) ? a_A : a_C; }
			return (a_B < a_C) ? a_B : a_C;
		}

		template <typename T>
		TB_INLINE T Max(T a_A, T a_B)
		{
			return (a_A > a_B) ? a_A : a_B;
		}

		template <typename T>
		TB_INLINE T Max(T a_A, T a_B, T a_C)
		{
			if (a_A > a_B) { return (a_A > a_C) ? a_A : a_C; }
			return (a_B > a_C) ? a_B : a_C;
		}

		TB_INLINE float Clamp(float a_Value, float a_Min, float a_Max)
		{
			return (a_Value > a_Max) ? a_Max : (a_Value < a_Min) ? a_Min : a_Value;
		}

		TB_INLINE float LERP(float a_Start, float a_End, float a_Percentage)
		{
			return (a_Start + (a_End - a_Start) * a_Percentage);
		}

		template <typename T>
		TB_INLINE T Equals(T a_Left, T a_Right, T a_Delta)
		{
			if (a_Left < (a_Right - a_Delta)) { return false; }
			if (a_Left > (a_Right + a_Delta)) { return false; }
			return true;
		}

		// ====================================
		// Angle math
		// ====================================

		//! Converts degrees (0 - 360) to radians (0 - 2 pi).
		TB_INLINE float DegToRad(float a_Degrees) { return (a_Degrees * pi_radians); }

		//! Converts radians (0 - 2 pi) to degrees (0 - 360).
		TB_INLINE float RadToDeg(float a_Radians) { return (a_Radians * pi_degrees); }

		//! Returns the sine of an amount of radians.
		TB_INLINE float Sin(float a_Radians) { return sinf(a_Radians); }

		//! Returns the sine of an amount of degrees. (You'll probably want this.)
		TB_INLINE float SinDeg(float a_Degrees) { return sinf(a_Degrees * pi_radians); }

		//! Returns the arc sine of an amount of radians.
		TB_INLINE float ArcSin(float a_Radians) { return asinf(a_Radians); }

		//! Returns the arc sine of an amount of degrees.
		TB_INLINE float ArcSinDeg(float a_Degrees) { return asinf(a_Degrees * pi_radians); }

		//! Returns the cosine of an amount of radians.
		TB_INLINE float Cos(float a_Radians) { return cosf(a_Radians); }

		//! Returns the cosine of an amount of degrees.
		TB_INLINE float CosDeg(float a_Degrees) { return cosf(a_Degrees * pi_radians); }

		//! Returns the arc cosine of an amount of radians.
		TB_INLINE float ArcCos(float a_Radians) { return acosf(a_Radians); }

		//! Returns the arc cosine of an amount of degrees.
		TB_INLINE float ArcCosDeg(float a_Degrees) { return acosf(a_Degrees * pi_radians); }

		//! Returns the tangent of an amount of radians.
		TB_INLINE float Tan(float a_Radians) { return tanf(a_Radians); }

		//! Returns the tangent of an amount of degrees.
		TB_INLINE float TanDeg(float a_Degrees) { return tanf(a_Degrees * pi_radians); }

		//! Returns the arc tangent of an amount of radians.
		TB_INLINE float ArcTan(float a_Radians) { return atanf(a_Radians); }

		//! Returns the arc tangent of an amount of degrees.
		TB_INLINE float ArcTanDeg(float a_Degrees) { return atanf(a_Degrees * pi_radians); }

		TB_INLINE float ArcTan2(float a_X, float a_Y) { return atan2f(a_X, a_Y); }

		// ====================================
		// Randomness
		// ====================================

		//! Seeds the random number generator
		/*!
			/param a_Seed The seed value

			If a seed of 0 is specified, the function inputs the current time in milliseconds as the seed.
		*/
		TB_INLINE void Seed(unsigned int a_Seed = 0) 
		{ 
			unsigned int seed = (a_Seed == 0) ? (unsigned int)(time(0)) : a_Seed;
			srand(seed);
		}

		//! Outputs a random number between 0 and 1.
		TB_INLINE float Random() 
		{ 
			return (((float)rand() / RAND_MAX)); 
		}

		//! Outputs a random number between 0 and a_Max.
		TB_INLINE float Random(float a_Max) 
		{ 
			return (((float)rand() / RAND_MAX) * (a_Max)); 
		}

		//! Outputs a random number between a_Min and a_Max.
		TB_INLINE float Random(float a_Min, float a_Max) 
		{ 
			return a_Min + (((float)rand() / RAND_MAX) * (a_Max - a_Min)); 
		}

		//! Outputs a random number between a_Min and a_Max.
		TB_INLINE int Random(int a_Min, int a_Max) 
		{ 
			// ahhh fixed point math, we meet again
			return a_Min + ((rand() * (a_Max - a_Min)) / RAND_MAX); 
		}

	}; // namespace Math

}; // namespace tb

#endif